/*
 * Copyright(C) 2009, kesalin All rights reserved.
 * 
 * File name : MainDlg.h
 * Brief     : Test dialog for drag and drop
 * Version   : 1.0
 * Author    : kesalin@gmail.com
 * Date      : 2009-6-5
 */

// MainDlg.h : interface of the CMainDlg class
//
/////////////////////////////////////////////////////////////////////////////
#pragma once

// include header file for drag and drop.
#include "include/LuoDnDAssist.h"

extern LuoDnDAssist::UActivator * g_pDnDAssist;

class CMainDlg :
	public CDialogImpl<CMainDlg>,
	public CUpdateUI<CMainDlg>,
	public CMessageFilter,
	public CIdleHandler {

	HWND m_hDnDTarget;

	WTL::CRect m_rcSource;

	bool m_bAcceptable;
	bool m_bOver;

	// clipboard format.
	static const UINT CF_FILECONTENTS;
	static const UINT CF_FILEGROUPDESCRIPTORW;
	static const UINT CF_HTMLFORMAT;

	CStringW testString;
public:
	enum {
		IDD = IDD_MAINDLG
	};

	CMainDlg() :
	m_hDnDTarget(0),
	m_rcSource(0, 0, 0, 0),
	m_bAcceptable(false),
	m_bOver(false)
	{
		testString.Format(L"Drag this text.");
	}

	~CMainDlg()
	{
	}

	virtual BOOL PreTranslateMessage(MSG* pMsg)
	{
		return CWindow::IsDialogMessage(pMsg);
	}

	virtual BOOL OnIdle()
	{
		return FALSE;
	}

	BEGIN_UPDATE_UI_MAP(CMainDlg)
	END_UPDATE_UI_MAP()

	BEGIN_MSG_MAP(CMainDlg)

	MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
	MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
	COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
	COMMAND_ID_HANDLER(IDOK, OnOK)
	COMMAND_ID_HANDLER(IDCANCEL, OnCancel)

	MSG_WM_MOUSEMOVE(OnMouseMove)
	MSG_WM_LBUTTONDOWN(OnLButtonDown)
	MSG_WM_PAINT(OnPaint)
	MSG_WM_CLOSE(OnClose)

	END_MSG_MAP()

	// Handler prototypes (uncomment arguments if needed):
	//	LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	//	LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	//	LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
	LRESULT OnInitDialog(UINT
	/*uMsg*/, WPARAM
	/*wParam*/, LPARAM
	/*lParam*/, BOOL &
	/*bHandled*/ )
	{
		// center the dialog on the screen
		CenterWindow();

		// set icons
		HICON hIcon = (HICON)::LoadImage(
				_Module.GetResourceInstance(),
				MAKEINTRESOURCE(IDR_MAINFRAME),
				IMAGE_ICON,
				::GetSystemMetrics(SM_CXICON),
				::GetSystemMetrics(SM_CYICON),
				LR_DEFAULTCOLOR);
		SetIcon(hIcon, TRUE);

		HICON hIconSmall = (HICON)::LoadImage(
				_Module.GetResourceInstance(),
				MAKEINTRESOURCE(IDR_MAINFRAME),
				IMAGE_ICON,
				::GetSystemMetrics(SM_CXSMICON),
				::GetSystemMetrics(SM_CYSMICON),
				LR_DEFAULTCOLOR);
		SetIcon(hIconSmall, FALSE);

		// register object for message filtering and idle updates
		CMessageLoop*  pLoop = _Module.GetMessageLoop();
		ATLASSERT(pLoop != NULL);
		pLoop->AddMessageFilter(this);
		pLoop->AddIdleHandler(this);

		UIAddChildWindowContainer(m_hWnd);

		// Initialize dndAssist.
		InitDndAssist();

		return TRUE;
	}

	LRESULT OnDestroy(UINT
	/*uMsg*/, WPARAM
	/*wParam*/, LPARAM
	/*lParam*/, BOOL &
	/*bHandled*/ )
	{
		// unregister message filtering and idle updates
		CMessageLoop*  pLoop = _Module.GetMessageLoop();
		ATLASSERT(pLoop != NULL);
		pLoop->RemoveMessageFilter(this);
		pLoop->RemoveIdleHandler(this);

		return 0;
	}

	LRESULT OnAppAbout(WORD
	/*wNotifyCode*/, WORD
	/*wID*/, HWND
	/*hWndCtl*/, BOOL &
	/*bHandled*/ )
	{
		CAboutDlg dlg;
		dlg.DoModal();
		return 0;
	}

	LRESULT OnOK(WORD
	/*wNotifyCode*/, WORD wID, HWND
	/*hWndCtl*/, BOOL &
	/*bHandled*/ )
	{
		// TODO: Add validation code
		CloseDialog(wID);
		return 0;
	}

	LRESULT OnCancel(WORD
	/*wNotifyCode*/, WORD wID, HWND
	/*hWndCtl*/, BOOL &
	/*bHandled*/ )
	{
		CloseDialog(wID);
		return 0;
	}

	void CloseDialog(int nVal)
	{
		UnitDnDAssist();

		DestroyWindow();
		::PostQuitMessage(nVal);
	}

	void OnClose()
	{
		UnitDnDAssist();

		DestroyWindow();
		::PostQuitMessage(0);
	}

	void OnPaint(HDC
	/*hDC*/ )
	{
		WTL::CPaintDC dc(*this); 
		
		Gdiplus::Graphics g(dc);

		Gdiplus::SolidBrush blackBrush(Gdiplus::Color(255, 0, 0, 0));
		g.FillRectangle(&blackBrush, m_rcSource.left, m_rcSource.top, m_rcSource.Width(), m_rcSource.Height());	

		// Initialize arguments.
		Gdiplus::Font myFont(L"Arial", 12);
		Gdiplus::RectF layoutRect(
			Gdiplus::REAL(m_rcSource.left), 
			Gdiplus::REAL(m_rcSource.top), 
			Gdiplus::REAL(m_rcSource.Width()), 
			Gdiplus::REAL(m_rcSource.Height()));
		Gdiplus::StringFormat format;
		format.SetAlignment(Gdiplus::StringAlignmentNear);
		Gdiplus::SolidBrush whiteBrush(Gdiplus::Color(255, 255, 255, 255));

		// Draw string.
		g.DrawString(
			testString,
			testString.GetLength(),
			&myFont,
			layoutRect,
			&format,
			&whiteBrush);
	}

	void OnMouseMove(UINT nFlags, WTL::CPoint pt)
	{
		if (m_rcSource.PtInRect(pt)) {
			HCURSOR csr = LoadCursor(0, IDC_HAND);
			SetCursor(csr);
		}
	}

	void OnLButtonDown(UINT nFlags, WTL::CPoint pt)
	{
		ATLTRACE("OnLButtonDown()\n");

		if (!m_rcSource.PtInRect(pt)) {
			return;
		}

		// do drag.
		LuoDnDAssist::DataItem item = {
			CF_UNICODETEXT,
			(LPCWSTR)testString,
			(testString.GetLength() + 1) * sizeof(WCHAR)
		};

		ATL::CComPtr<IDataObject> dobj;
		HRESULT hRslt = g_pDnDAssist->CreateDataObject(&item, 1, &dobj);
		if (FAILED(hRslt)) {
			return;
		}

		ATL::CComPtr<IDropSource> dsrc;
		hRslt = g_pDnDAssist->CreateDropSource(&dsrc);
		if (FAILED(hRslt)) {
			return;
		}

		DWORD dwEffect = 0;
		hRslt = DoDragDrop(dobj, dsrc, DROPEFFECT_COPY, &dwEffect);
		if (FAILED(hRslt)) {
			return;
		}

		ATLTRACE("Dragging...\n");
	}

	//////////////////////////////////////////////////////////////////////////
	void InitDndAssist()
	{
		HWND hTarget = GetDlgItem(IDC_TARGET);

		::SetWindowText(hTarget, _T("Drop to here."));

		WTL::CRect rcTarg;
		::GetWindowRect(hTarget, &rcTarg);

		WTL::CPoint pt = rcTarg.TopLeft();
		ScreenToClient(&pt);

		WTL::CSize sz = rcTarg.Size();

		WTL::CRect rcDlg;
		GetClientRect(&rcDlg);

		int ax = rcDlg.Width() / 2;
		int dx = ax - (pt.x + sz.cx);

		int x = ax + dx;
		int y = pt.y;

		m_rcSource = WTL::CRect(WTL::CPoint(x, y), sz);

		// setup drag and drop.
		m_hDnDTarget = *this;

		ATL::CComPtr<IDropTarget> targ;
		HRESULT hRslt = g_pDnDAssist->CreateDropTarget(this, &CMainDlg::DropCallback, &targ);
		if (SUCCEEDED(hRslt)) {
			hRslt = RegisterDragDrop(m_hDnDTarget, targ);
			if (SUCCEEDED(hRslt)) {
				ATLTRACE("RegisterDragDrop() OK.\n");
			}
		}
	}

	void UnitDnDAssist()
	{
		// clear drag and drop
		if (m_hDnDTarget != NULL) {
			RevokeDragDrop(m_hDnDTarget);
			m_hDnDTarget = NULL;
		}
	}

/**
* @brief      : Callback for drag and drop.
* @param      : 
* @return     : 
*/
	static HRESULT STDCALL DropCallback(void *p, int type, LuoDnDAssist::DropParam * para)
	{
		CMainDlg*  pThis = static_cast<CMainDlg*>(p);
		return pThis->OnDropCallback(type, para);
	}

	HRESULT OnDropCallback(int type, LuoDnDAssist::DropParam * para)
	{
		switch (type) {
		case LuoDnDAssist::DT_Enter: {
				ATLTRACE("OnDropCallback() : DT_Enter\n");
				m_bAcceptable = false;
				m_bOver = false;

				IDataObject*  dobj = para->pDataObj;

				/* */
				if (dobj != 0) {
					ATL::CComPtr<IEnumFORMATETC> ef;
					HRESULT hRslt = dobj->EnumFormatEtc(DATADIR_GET, &ef);
					if (SUCCEEDED(hRslt)) {
						for (;;) {
							FORMATETC fe = { 0};

							ULONG cFetched = 0;
							hRslt = ef->Next(1, &fe, &cFetched);
							if (hRslt != S_OK || cFetched != 1) {
								break;
							}

							CLIPFORMAT cf = fe.cfFormat;
							WCHAR buf[256] = {0 ,};
							GetClipboardFormatNameW(cf, buf, 256);

							ATLTRACE(" FMT: %S (0x%04x)\n", buf, cf);
						}
					}
				}

				if (dobj != 0) {
					FORMATETC fmt = { CF_FILECONTENTS, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
					HRESULT hRslt = dobj->QueryGetData(&fmt);
					if (SUCCEEDED(hRslt)) {
						ATLTRACE("CF_FILECONTENTS OK.\n");
					}
				}

				if (dobj != 0) {
					FORMATETC fmt = { CF_FILECONTENTS, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL };
					STGMEDIUM med = { 0};

					HRESULT hRslt = dobj->GetData(&fmt, &med);
					if (SUCCEEDED(hRslt)) {
						if (med.tymed == TYMED_HGLOBAL) {
							void*  ss = GlobalLock(med.hGlobal);
							if (ss != 0) {
								SIZE_T cb = GlobalSize(med.hGlobal);
								if (cb > 0) {
									ATLTRACE("CF_FILECONTENTS Size: %d\n", int(cb));
								}

								GlobalUnlock(med.hGlobal);
							}
						}

						ReleaseStgMedium(&med);
					}
				}

				/* */
				if (dobj != 0) {
					FORMATETC fmt = { CF_FILEGROUPDESCRIPTORW, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL };
					STGMEDIUM med = { 0};

					HRESULT hRslt = dobj->GetData(&fmt, &med);
					if (SUCCEEDED(hRslt)) {
						if (med.tymed == TYMED_HGLOBAL) {
							void*  ss = GlobalLock(med.hGlobal);
							if (ss != 0) {
								SIZE_T cb = GlobalSize(med.hGlobal);
								if (cb > 0) {
									ATLTRACE("CF_FILEGROUPDESCRIPTORW Size: %d\n", int(cb));

									FILEGROUPDESCRIPTORW*  fgd = static_cast<FILEGROUPDESCRIPTORW*>(ss);

									int files = fgd->cItems;
									ATLTRACE("  Files: %d\n", files);

									for (int i = 0; i < files; i++) {
										FILEDESCRIPTORW*  fd = &(fgd->fgd[i]);
										CStringA fn(fd->cFileName);
										ATLTRACE("  %d -> %s\n", i, (const char*)fn);
									}
								}

								GlobalUnlock(med.hGlobal);
							}
						}

						ReleaseStgMedium(&med);
					}
				}

				/* */
				if (dobj != 0) {
					FORMATETC fmt = { CF_HTMLFORMAT, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL };
					STGMEDIUM med = { 0};

					HRESULT hRslt = dobj->GetData(&fmt, &med);
					if (SUCCEEDED(hRslt)) {
						if (med.tymed == TYMED_HGLOBAL) {
							void*  ss = GlobalLock(med.hGlobal);
							if (ss != 0) {
								SIZE_T cb = GlobalSize(med.hGlobal);
								if (cb > 0) {
									ATLTRACE("CF_HTMLFORMAT Size: %d\n", int(cb));
								}

								GlobalUnlock(med.hGlobal);
							}
						}

						ReleaseStgMedium(&med);
					}
				}

				/* */
				if (dobj != 0) {
					FORMATETC fmt = { CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
					HRESULT hRslt = dobj->QueryGetData(&fmt);
					if (SUCCEEDED(hRslt)) {
						m_bAcceptable = true;
					}
				}

#if 0
				if (dobj != 0) {
					FORMATETC fmt = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
					HRESULT hRslt = dobj->QueryGetData(&fmt);
					if (SUCCEEDED(hRslt)) {
						m_bAcceptable = false;
					}
				}
#endif
				break;
			}

		case LuoDnDAssist::DT_Over:
			if (!m_bOver) {
				ATLTRACE("OnDropCallback() : DT_Over\n");
				m_bOver = true;
			}
			break;
		case LuoDnDAssist::DT_Leave:
			ATLTRACE("OnDropCallback() : DT_Leave\n");
			m_bAcceptable = false;
			m_bOver = false;
			return S_OK;
		case LuoDnDAssist::DT_Drop:
			ATLTRACE("OnDropCallback() : DT_Drop\n");
			break;
		case LuoDnDAssist::DT_FinalRelease:
			ATLTRACE("OnDropCallback() : DT_FinalRelease\n");
			return S_OK;
		}

		WTL::CPoint pt(para->x, para->y);

		HWND hTarget = GetDlgItem(IDC_TARGET);

		WTL::CRect rcTarg;
		::GetWindowRect(hTarget, &rcTarg);

		bool bActive = false;
		if (rcTarg.PtInRect(pt)) {
			bActive = true;
		}

		bool bGo = m_bAcceptable && bActive && (para->dwEffectMask & DROPEFFECT_COPY) != 0;
		if (bGo) {
			para->dwEffect = DROPEFFECT_COPY;
		}
		else {
			para->dwEffect = DROPEFFECT_NONE;
		}

		if (bGo && type == LuoDnDAssist::DT_Drop) {
			IDataObject*  dobj = para->pDataObj;
			if (dobj != 0) {
				FORMATETC fmt = { CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
				STGMEDIUM med = { 0};

				CStringW data;

				HRESULT hRslt = dobj->GetData(&fmt, &med);
				if (SUCCEEDED(hRslt)) {
					if (med.tymed == TYMED_HGLOBAL) {
						void*  ss = GlobalLock(med.hGlobal);
						if (ss != 0) {
							SIZE_T cb = GlobalSize(med.hGlobal);
							if (cb > 0) {
								int len = int(cb / sizeof(WCHAR)) - 1;
								if (len > 0) {
									data = CStringW (static_cast<LPCWSTR>(ss), len);
								}
							}

							GlobalUnlock(med.hGlobal);
						}
					}

					ReleaseStgMedium(&med);
				}

				SetDlgItemTextW(IDC_TARGET, data);
			}
		}

		return S_OK;
	}
};
